/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights
 * reserved. <a>http://www.cqcis.com</a>
 */
package com.cqcis.uip.base.framework.service.impl;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Set;

import org.apache.commons.httpclient.ConnectTimeoutException;
import org.apache.commons.httpclient.params.HttpConnectionParams;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;

import com.cqcis.uip.base.framework.UIPServiceException;

/**
 * HTTP客户端Socket factory实现
 * 
 * @author huangmy
 * @date   2008-10-27
 */
public class HttpClientProtocolSocketFactoryImpl implements
		ProtocolSocketFactory {
	private long connectTimeout; // 连接超时时间
	
	public HttpClientProtocolSocketFactoryImpl(long connectTimeout) {
		this.connectTimeout = connectTimeout;
	}

	public Socket createSocket(String host, int port) throws IOException,
			UnknownHostException {
		return new Socket(host, port);
	}

	public Socket createSocket(String host, int port, InetAddress localAddress, int localPort)
			throws IOException, UnknownHostException {		
		return new Socket(host, port, localAddress, localPort);
	}

	public Socket createSocket(String host, int port, InetAddress localAddress,
			int localPort, HttpConnectionParams params) throws IOException,
			UnknownHostException, ConnectTimeoutException {
		if (params == null) {
            throw new IllegalArgumentException("Parameters may not be null");
        }
        int timeout = params.getConnectionTimeout();
        if (timeout == 0) {
            return createSocket(host, port, localAddress, localPort);
        } else {     
        	InetSocketAddress local = null;
        	if (localAddress != null) {
        		local = new InetSocketAddress(localAddress, localPort);
        	}
        	
        	if (this.connectTimeout <= 0) {
        		this.connectTimeout = (long)timeout;
        	}
        	
            return createSocketChannel(new InetSocketAddress(host, port), 
            		local);
        }
	}
	
	/**
	 * 创建socketChannel
	 * 
	 * @param address
	 * @param localAddress
	 * @return
	 * @throws IOException
	 */
	private Socket createSocketChannel(SocketAddress address, SocketAddress localAddress) throws IOException {
		SocketChannel ch = SocketChannel.open();
        ch.socket().setReuseAddress(true);
        if (localAddress != null) {
            ch.socket().bind(localAddress);
        }
        
        ch.configureBlocking(true);
        
        if (ch.connect(address)) { // 马上完成连接
        	return ch.socket();
        }
        
        // 异步完成连接
        Selector selector = null;

		try {
			selector = Selector.open();
			ch.register(selector, SelectionKey.OP_CONNECT, null);
			int nKeys = selector.select(this.connectTimeout);
			if (nKeys > 0) {
				Set<SelectionKey> keys = selector.selectedKeys();
				if (keys != null) {
					for (SelectionKey key : keys) {
						SocketChannel chconn = (SocketChannel) key.channel();
						if (ch.finishConnect()) {
							key.cancel();
							return chconn.socket();
						}
					}

					keys.clear();
				}
			}
		} catch (Throwable t) {
			try {
			    ch.close();		
			} catch (Throwable tc) {				
			}
			throw new UIPServiceException(t);
		} finally {
			try {
			    selector.close();
			} catch (Throwable tc) {				
			}
		}
		
		try {
		    ch.close();		
		} catch (Throwable tc) {				
		}
    	throw new UIPServiceException("连接超时:http connect timeout");
	}
}
